package fuck.livenessdetectionxposed;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

import static de.robv.android.xposed.XposedHelpers.callMethod;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import static de.robv.android.xposed.XposedHelpers.getObjectField;
import static de.robv.android.xposed.XposedHelpers.setObjectField;

/**
 * Created by xuxingcan on 16-11-20.
 */
public class XposedInit implements IXposedHookLoadPackage {
    private static final String fuckSelectedPic = "fuckSelectedPic";
    private static final String fuckCropBitmap = "fuckCropBitmap";
    private static final int REQUEST_CODE_CROP = 101;
    private static final int REQUEST_CODE_GET = 102;
    private boolean isBitmapBytesDone = false;
    private String idCodeString;
    private String nameString;

    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        if (!loadPackageParam.packageName.contains("com.sinovatech.unicom.ui")) {
            return;
        }
        try {
//            XposedHelpers.findAndHookMethod("com.oliveapp.face.livenessdetectorsdk.d.b.b", loadPackageParam.classLoader, "a", Object.class, String.class, Throwable.class, new XC_MethodHook() {
//                @Override
//                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
//                    super.beforeHookedMethod(param);
//                    XposedBridge.log(param.args[0] + ":" + param.args[1] + "\n" + Log.getStackTraceString((Throwable) param.args[2]));
//                }
//            });
//            XposedHelpers.findAndHookMethod("com.oliveapp.face.livenessdetectorsdk.d.b.b", loadPackageParam.classLoader, "b", Object.class, String.class, new XC_MethodHook() {
//                @Override
//                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
//                    super.beforeHookedMethod(param);
//                    if ("ImageUtil".equals(param.args[0])) {
//                        try {
//                            throw new Exception("ImageUtil");
//                        } catch (Exception e) {
//                            XposedBridge.log(param.args[0] + ":" + param.args[1]);
//                            XposedBridge.log(param.args[0] + ":" + Log.getStackTraceString(e));
//                        }
//                    } else {
//                        XposedBridge.log(param.args[0] + ":" + param.args[1]);
//                    }
//                }
//            });
//            XposedHelpers.findAndHookMethod("com.oliveapp.face.livenessdetectorsdk.d.b.b", loadPackageParam.classLoader, "c", Object.class, String.class, new XC_MethodHook() {
//                @Override
//                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
//                    super.beforeHookedMethod(param);
//                    if ("ImageUtil".equals(param.args[0])) {
//                        try {
//                            throw new Exception("ImageUtil");
//                        } catch (Exception e) {
//                            XposedBridge.log(param.args[0] + ":" + param.args[1]);
//                            XposedBridge.log(param.args[0] + ":" + Log.getStackTraceString(e));
//                        }
//                    } else {
//                        XposedBridge.log(param.args[0] + ":" + param.args[1]);
//                    }
//                }
//            });
            XposedHelpers.findAndHookMethod("com.gzt.faceid5sdk.a", loadPackageParam.classLoader, "a", byte[].class, new XC_MethodReplacement() {
                @Override
                protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                    if (isBitmapBytesDone) {
                        isBitmapBytesDone = false;
                        Object a = getObjectField(param.thisObject, "a");
                        byte[] bytes = getBitmapFileBytes();
                        callMethod(a, "onFaceImageCaptured", bytes);
                        setObjectField(param.thisObject, "b", null);
                        XposedBridge.log("replace onFaceImageCaptured");
                    } else {
//                        Object a = getObjectField(param.thisObject, "a");
//                        byte[] bytes = (byte[]) param.args[0];
//                        callMethod(a, "onFaceImageCaptured", bytes);
//                        setObjectField(param.thisObject, "b", null);
                    }
                    try {
                        throw new Exception("onFaceImageCaptured");
                    } catch (Exception e) {
                        XposedBridge.log(e);
                    }
                    return null;
                }
            });
            XposedHelpers.findAndHookMethod("com.gzt.faceid5sdk.a", loadPackageParam.classLoader, "a", Context.class, String.class, String.class, new XC_MethodReplacement() {
                @Override
                protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                    idCodeString = (String) methodHookParam.args[1];
                    nameString = (String) methodHookParam.args[2];
                    Activity activity = (Activity) methodHookParam.args[0];
                    activity.getIntent().putExtra(fuckSelectedPic, true);
                    Intent intent = new Intent();
                    intent.setType("image/*");
                    intent.setAction(Intent.ACTION_GET_CONTENT);
                    activity.startActivityForResult(intent, REQUEST_CODE_GET);
                    return null;
                }
            });
//            XposedHelpers.findAndHookMethod("com.gzt.faceid5sdk.LivenessDetectionActivity", loadPackageParam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() {
//                @Override
//                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//                    super.afterHookedMethod(param);
//                    final Activity activity = (Activity) param.thisObject;
//                    View headIcon = (View) XposedHelpers.getObjectField(param.thisObject, "f");
//                    headIcon.setOnClickListener(new View.OnClickListener() {
//                        @Override
//                        public void onClick(View v) {
//                            activity.getIntent().putExtra(fuckSelectedPic, true);
//                            Intent intent = new Intent();
//                            intent.setType("image/*");
//                            intent.setAction(Intent.ACTION_GET_CONTENT);
//                            activity.startActivityForResult(intent, REQUEST_CODE_GET);
//                        }
//                    });
//                }
//            });
            XposedHelpers.findAndHookMethod(Activity.class, "onActivityResult", int.class, int.class, Intent.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    Activity activity = (Activity) param.thisObject;
                    int requestCode = (int) param.args[0];
                    int resultCode = (int) param.args[1];
                    Intent data = (Intent) param.args[2];
                    if (activity.getIntent().getBooleanExtra(fuckSelectedPic, false)) {
                        activity.getIntent().putExtra(fuckSelectedPic, false);
                        if (requestCode == REQUEST_CODE_GET && resultCode == Activity.RESULT_OK) {
                            activity.getIntent().putExtra(fuckCropBitmap, true);
                            File file = new File(Environment.getExternalStorageDirectory(), "/crop.png");
                            Uri uri = data.getData();
                            Intent intent = new Intent("com.android.camera.action.CROP");
                            intent.setDataAndType(uri, "image/*");
                            intent.putExtra("crop", "true");
                            intent.putExtra("aspectX", 1);
                            intent.putExtra("aspectY", 1);
                            intent.putExtra("outputX", 305);
                            intent.putExtra("outputY", 305);
                            intent.putExtra("return-data", false);
                            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
                            activity.startActivityForResult(intent, REQUEST_CODE_CROP);
//                            Object i = XposedHelpers.getObjectField(param.thisObject, "i");
//                            XposedHelpers.callMethod(i, "a", bytes);
//                            activity.finish();
                        }
                    } else if (activity.getIntent().getBooleanExtra(fuckCropBitmap, false)) {
                        activity.getIntent().putExtra(fuckCropBitmap, false);
                        if (requestCode == REQUEST_CODE_CROP && resultCode == Activity.RESULT_OK) {
                            isBitmapBytesDone = true;
                            Intent intent = new Intent();
                            intent.setClassName(activity.getPackageName(), "com.gzt.faceid5sdk.LivenessDetectionActivity");
                            Bundle bundle = new Bundle();
                            if (TextUtils.isEmpty(idCodeString)) {
                                idCodeString = "";
                            }
                            bundle.putString("idCode", idCodeString);
                            if (TextUtils.isEmpty(nameString)) {
                                nameString = "";
                            }
                            bundle.putString("name", nameString);
                            intent.putExtra("userInfo", bundle);
                            idCodeString = "";
                            nameString = "";
                            activity.startActivity(intent);
                        }
                    }
                }
            });
            findAndHookMethod("com.oliveapp.face.livenessdetectorsdk.d.b.a", loadPackageParam.classLoader, "a", Bitmap.class, int.class, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
//                    Bitmap bitmap = (Bitmap) param.args[0];
//                    saveBitmapFile(bitmap);
                    XposedBridge.log("replace from ImageUtil");
//                    param.setResult(getBitmapFileBytes());
                    File file = new File(Environment.getExternalStorageDirectory(), "/crop.png");
                    Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                    Bitmap argBitmap = (Bitmap) param.args[0];
                    Bitmap nBitmap = corpBitmap(bitmap, argBitmap.getWidth(), argBitmap.getHeight());
                    if (!nBitmap.equals(bitmap)) {
                        bitmap.recycle();
                    }
                    param.setResult(bitmap2Bytes(nBitmap, (Integer) param.args[1]));
                    nBitmap.recycle();
                }
            });
//            hookActivity(loadPackageParam.classLoader);
        } catch (Exception e) {
            XposedBridge.log(e);
        }
    }

    public void saveBitmapFile(Bitmap bitmap) {
        File dir = new File(Environment.getExternalStorageDirectory(), "livenessdetectorsdk");
        if (!dir.exists()) {
            dir.mkdir();
        }
        File file = new File(dir, System.currentTimeMillis() + ".png");
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
            bos.flush();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void hookActivity(ClassLoader loader) {
        findAndHookMethod("android.app.Activity", loader, "onActivityResult", int.class, int.class, Intent.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                int requestCode = (int) param.args[0];
                int resultCode = (int) param.args[1];
                Intent mIntent = (Intent) param.args[2];
                XposedBridge.log("requestCode = " + requestCode);
                XposedBridge.log("resultCode = " + resultCode);
                XposedBridge.log("getExtras = " + mIntent.getExtras());
            }
        });
        findAndHookMethod("android.app.Activity", loader, "setResult", int.class, Intent.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                try {
                    throw new Exception("setResult");
                } catch (Exception e) {
                    XposedBridge.log(e);
                }
            }
        });
        findAndHookMethod("android.app.Activity", loader, "setResult", int.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                try {
                    throw new Exception("setResult");
                } catch (Exception e) {
                    XposedBridge.log(e);
                }
            }
        });
        findAndHookMethod("android.app.Activity", loader, "startActivityForResult", Intent.class, int.class, Bundle.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                try {
                    Intent mIntent = (Intent) param.args[0];
                    XposedBridge.log("getExtras = " + mIntent.getExtras());
                    XposedBridge.log("getClassName = " + mIntent.getComponent().getClassName());
                    throw new Exception("startActivityForResult");
                } catch (Exception e) {
                    XposedBridge.log(e);
                }
            }
        });
        findAndHookMethod("android.app.Activity", loader, "startActivity", Intent.class, Bundle.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                try {
                    Intent mIntent = (Intent) param.args[0];
                    XposedBridge.log("getExtras = " + mIntent.getExtras());
                    XposedBridge.log("getClassName = " + mIntent.getComponent().getClassName());
                    throw new Exception("startActivity");
                } catch (Exception e) {
                    XposedBridge.log(e);
                }
            }
        });
    }

    public byte[] getBitmapFileBytes() {
        File file = new File(Environment.getExternalStorageDirectory(), "/crop.png");
        Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
        if (bitmap.getWidth() != 305) {
            Bitmap nBitmap = corpBitmap(bitmap, 305, 305);
            if (!nBitmap.equals(bitmap)) {
                bitmap.recycle();
            }
            bitmap = nBitmap;
        }
        byte[] bytes = bitmap2Bytes(bitmap, 70);
        bitmap.recycle();
        return bytes;
    }

    public static byte[] bitmap2Bytes(Bitmap bitmap, int i) {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, i, byteArrayOutputStream);
            byte[] toByteArray = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.flush();
            byteArrayOutputStream.close();
            return toByteArray;
        } catch (Throwable e) {
            XposedBridge.log(e);
            return null;
        }
    }

    /**
     * 用尽可能低的内存消耗裁剪获取指定大小的bitmap。
     * <p/>
     * 不保持原始图片的宽高比，因此确保返回bitmap的尺寸与设定的相同。
     * <p/>
     */
    public static Bitmap corpBitmap(Bitmap tempBitmap, int reqWidth, int reqHeight) {
        Bitmap resizeBmp;
        Matrix matrix = new Matrix();
        float scaleWidth = (float) reqWidth / (float) tempBitmap.getWidth();
        float scaleHeight = (float) reqHeight / (float) tempBitmap.getHeight();
        matrix.postScale(scaleWidth, scaleHeight);
        resizeBmp = Bitmap.createBitmap(tempBitmap, 0, 0, tempBitmap.getWidth(), tempBitmap.getHeight(), matrix, true);
        return resizeBmp;
    }
}
